﻿package com.pandemicode.framework.localization {
	import com.pandemicode.framework.xml.XMLLoader;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.text.Font;
	import flash.utils.getDefinitionByName;
	import org.osflash.signals.natives.NativeSignal;
	import org.osflash.signals.Signal;
	
	/**
	* @author Michael Lawrence
	* @version 1.0.0
	*/
	public class Locale {
		private var _language:String;
		private var _xmlPath:String;
		private var _fontPath:String;
		private var _strings:Array;
		private var _fontClassName:String;
		private var _font:Font;
		private var _fontLoader:Loader;
		private var _xmlFile:String;
		private var _xml:XMLLoader;
		private var _isLoaded:Boolean = false;
		
		private var _fontLoaded:NativeSignal;
		private var _loaded:Signal;
		
		public function get loaded():Signal { return this._loaded; }
		
		/**
		 * Whether the Locale has completed loading.
		 * @default false
		 */
		public function get isLoaded():Boolean { return this._isLoaded; }
		
		/**
		 * The language code.
		 * @default en
		 */
		public function get language():String { return this._language; }
		
		/**
		 * An array of all the strings this locale contains.
		 */
		public function get strings():Array { return this._strings; }
		
		/**
		 * The full class path of the font.
		 */
		public function get fontClassName():String { return this._fontClassName; }
		
		/**
		 * An instance of the font to use for this locale.
		 */
		public function get font():Font { return this._font; }
		
		/**
		 * A locale object that contains the strings and font for localization.
		 * @param	language	The language code to load the strings and font from.
		 * @param	xmlPath		The path to load the xml from ($language is replaced with the specified langauge).
		 * @param	fontPath	The path to load the font from ($language is replaced with the specified langauge).
		 */
		public function Locale(language:String = "en", xmlPath:String = "locale/$language/strings.xml", fontPath:String = "locale/$language/font.swf") {
			this._language = language;
			this._xmlPath = xmlPath;
			this._fontPath = fontPath;
			
			this._loaded = new Signal(Locale);
		}
		
		/**
		 * Loads the locale.
		 */
		public function load():void {
			this._xml = new XMLLoader(this.stringReplace(this._xmlPath));
			this._xml.xmlLoadCompleted.add(this._xmlLoaderComplete);
		}
		
		/**
		 * Returns the string specified by the id.
		 * @param	id	The id of the string to load.
		 * @return	Returns the string specified by the id.
		 */
		public function getString(id:String):String {
			for each(var ls:LocaleString in this.strings)
				if (ls.id == id) return ls.value;
			return null;
		}
		
		/**
		 * Occurs when the xml is done loading.
		 * @param	e	The dispatched event.
		 */
		private function _xmlLoaderComplete(document:XML):void {
			this._xml.xmlLoadCompleted.remove(this._xmlLoaderComplete);
			
			this._fontClassName = String(document.@font);
			this._strings = LocaleString.fromXMLList(document.string);
			
			this._loadFont();
		}
		
		/**
		 * Begins loading the font.
		 */
		private function _loadFont():void {
			this._fontLoader = new Loader();
			var fontRequest:URLRequest = new URLRequest(this.stringReplace(this._fontPath));
			this._fontLoaded = new NativeSignal(this._fontLoader.contentLoaderInfo, Event.COMPLETE, Event);
			this._fontLoaded.add(this._onFontLoaded);
			this._fontLoader.load(fontRequest);
		}
		
		/**
		 * Occurs when the font completes loading.
		 * @param	e	The dispatched font.
		 */
		private function _onFontLoaded(e:Event):void {
			try {
				var c:Class = this._fontLoader.contentLoaderInfo.applicationDomain.getDefinition(this._fontClassName) as Class;
				var o:* = new c();
				if (o is Font) {
					this._font = o as Font;
					Font.registerFont(c);
				} else throw new Error(this._fontClassName + " is not a Font.");
			} catch (e:Error) {
				trace(e.toString());
			}
			
			this._isLoaded = true;
			this._loaded.dispatch(this);
		}
		
		/**
		 * Replaces the variables within the string.
		 * @param	s	The string to replace.
		 * @return	Returns a string with the variables replaced.
		 */
		private function stringReplace(s:String):String {
			var re:RegExp = new RegExp("\\$language", "gi");
			return s.replace(re, this.language);
		}
	}
}
